package com.alawn.framework.core.web.faces.context;

import java.io.IOException;
import java.util.Iterator;

import javax.faces.FacesException;
import javax.faces.FactoryFinder;
import javax.faces.application.ViewExpiredException;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.event.ExceptionQueuedEvent;
import javax.faces.event.ExceptionQueuedEventContext;
import javax.faces.render.RenderKit;
import javax.faces.render.RenderKitFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.primefaces.PrimeFaces;

public class ViewExpiredExceptionHandler extends ExceptionHandlerWrapper {

	private ExceptionHandler wrapped;

	@SuppressWarnings("deprecation")
	public ViewExpiredExceptionHandler(ExceptionHandler wrapped) {
		this.wrapped = wrapped;
	}

	public ExceptionHandler getWrapped() {
		return this.wrapped;
	}

	public void handle() throws FacesException {
		Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents().iterator();
		while (iter.hasNext()) {
			ExceptionQueuedEvent event = (ExceptionQueuedEvent) iter.next();
			ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
			Throwable exception = context.getException();
			if ((exception instanceof ViewExpiredException)) {
				ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
				String url = (String) externalContext.getRequestHeaderMap().get("referer");
				if (url == null) {
					url = externalContext.getRequestContextPath() + ((ViewExpiredException) exception).getViewId();
				}
				redirect(url);
				iter.remove();
			}
		}
		getWrapped().handle();
	}

	private void redirect(String url) throws FacesException {
		try {
			FacesContext facesContext = FacesContext.getCurrentInstance();
			ExternalContext externalContext = facesContext.getExternalContext();
			if (externalContext.getRequestParameterMap().containsKey("javax.faces.partial.execute")) {
				if (!((String) externalContext.getRequestParameterMap().get("javax.faces.partial.execute"))
						.equals("@all")) {
					facesContext.setViewRoot(new UIViewRoot());
				}
			}
			if (((PrimeFaces.current().isAjaxRequest()) || (facesContext.getPartialViewContext().isPartialRequest()))
					&& (facesContext.getResponseWriter() == null) && (facesContext.getRenderKit() == null)) {
				ServletResponse response = (ServletResponse) externalContext.getResponse();
				ServletRequest request = (ServletRequest) externalContext.getRequest();
				response.setCharacterEncoding(request.getCharacterEncoding());

				RenderKitFactory factory = (RenderKitFactory) FactoryFinder
						.getFactory("javax.faces.render.RenderKitFactory");
				RenderKit renderKit = factory.getRenderKit(facesContext,
						facesContext.getApplication().getViewHandler().calculateRenderKitId(facesContext));
				ResponseWriter responseWriter = renderKit.createResponseWriter(response.getWriter(), null,
						request.getCharacterEncoding());
				facesContext.setResponseWriter(responseWriter);
			}
			externalContext.redirect(url);
		} catch (IOException e) {
			throw new FacesException(e);
		}
	}

}